#include "stdafx.h"
#include "ImgFourierTransform.h"
#include "bitmapData.h"
#include "HeapAllocator.h"
#include "lockHelper.h"

using namespace FTLib;

static void InternalTransform(std::shared_ptr<IComplexToComplexInv_Complex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	auto srcLocker = Locker(input.get());
	auto dstLocker = Locker(output.get());

	FourierTransfComplexInput2d ftInp;
	ftInp.width = input->GetWidth();
	ftInp.height = input->GetHeight();
	ftInp.pitch = srcLocker.GetLockInfo().pitch;
	ftInp.ptrData = (const float*)srcLocker.GetLockInfo().ptrDataRoi;

	FourierTransfComplexOutput2d ftOutp;
	ftOutp.width = output->GetWidth();
	ftOutp.height = output->GetHeight();
	ftOutp.pitch = dstLocker.GetLockInfo().pitch;
	ftOutp.ptrData = (float*)dstLocker.GetLockInfo().ptrDataRoi;

	transf->ComplexToComplex_Complex(ftInp, ftOutp, pAdditional);
}

static void InternalTransform(std::shared_ptr<IComplexToComplexFwd_Complex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	auto srcLocker = Locker(input.get());
	auto dstLocker = Locker(output.get());

	FourierTransfComplexInput2d ftInp;
	ftInp.width = input->GetWidth();
	ftInp.height = input->GetHeight();
	ftInp.pitch = srcLocker.GetLockInfo().pitch;
	ftInp.ptrData = (const float*)srcLocker.GetLockInfo().ptrDataRoi;

	FourierTransfComplexOutput2d ftOutp;
	ftOutp.width = output->GetWidth();
	ftOutp.height = output->GetHeight();
	ftOutp.pitch = dstLocker.GetLockInfo().pitch;
	ftOutp.ptrData = (float*)dstLocker.GetLockInfo().ptrDataRoi;

	transf->ComplexToComplex_Complex(ftInp, ftOutp, pAdditional);
}

static void InternalTransform(std::shared_ptr<IRealToComplex_Complex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	auto srcLocker = Locker(input.get());
	auto dstLocker = Locker(output.get());

	FourierTransfRealInputData2d ftInp;
	ftInp.width = input->GetWidth();
	ftInp.height = input->GetHeight();
	ftInp.pitch = srcLocker.GetLockInfo().pitch;
	ftInp.ptrData = (const float*)srcLocker.GetLockInfo().ptrDataRoi;

	FourierTransfComplexOutput2d ftOutp;
	ftOutp.width = output->GetWidth();
	ftOutp.height = output->GetHeight();
	ftOutp.pitch = dstLocker.GetLockInfo().pitch;
	ftOutp.ptrData = (float*)dstLocker.GetLockInfo().ptrDataRoi;

	transf->RealToComplex_Complex(ftInp, ftOutp, pAdditional);
}

static void InternalTransform(std::shared_ptr<IRealToComplex_PackedIppComplex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	auto srcLocker = Locker(input.get());
	auto dstLocker = Locker(output.get());

	FourierTransfRealInputData2d ftInp;
	ftInp.width = input->GetWidth();
	ftInp.height = input->GetHeight();
	ftInp.pitch = srcLocker.GetLockInfo().pitch;
	ftInp.ptrData = (const float*)srcLocker.GetLockInfo().ptrDataRoi;

	FourierTransfComplexOutput2d ftOutp;
	ftOutp.width = output->GetWidth();
	ftOutp.height = output->GetHeight();
	ftOutp.pitch = dstLocker.GetLockInfo().pitch;
	ftOutp.ptrData = (float*)dstLocker.GetLockInfo().ptrDataRoi;

	transf->RealToComplex_PackedIppComplex(ftInp, ftOutp, pAdditional);
}

static void InternalTransform(std::shared_ptr<IRealToComplex_PackedFftwComplex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	auto srcLocker = Locker(input.get());
	auto dstLocker = Locker(output.get());

	FourierTransfRealInputData2d ftInp;
	ftInp.width = input->GetWidth();
	ftInp.height = input->GetHeight();
	ftInp.pitch = srcLocker.GetLockInfo().pitch;
	ftInp.ptrData = (const float*)srcLocker.GetLockInfo().ptrDataRoi;

	FourierTransfComplexOutput2d ftOutp;
	ftOutp.width = output->GetWidth();
	ftOutp.height = output->GetHeight();
	ftOutp.pitch = dstLocker.GetLockInfo().pitch;
	ftOutp.ptrData = (float*)dstLocker.GetLockInfo().ptrDataRoi;

	transf->RealToComplex_PackedFftwComplex(ftInp, ftOutp, pAdditional);
}

static void InternalTransform(std::shared_ptr<IComplexHermitianToReal> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	auto srcLocker = Locker(input.get());
	auto dstLocker = Locker(output.get());

	FourierTransfComplexInput2d ftInp;
	ftInp.width = input->GetWidth();
	ftInp.height = input->GetHeight();
	ftInp.pitch = srcLocker.GetLockInfo().pitch;
	ftInp.ptrData = (const float*)srcLocker.GetLockInfo().ptrDataRoi;

	FourierTransfRealOutputData2d ftOutp;
	ftOutp.width = output->GetWidth();
	ftOutp.height = output->GetHeight();
	ftOutp.pitch = dstLocker.GetLockInfo().pitch;
	ftOutp.ptrData = (float*)dstLocker.GetLockInfo().ptrDataRoi;

	transf->ComplexHermitianToReal(ftInp, ftOutp, pAdditional);
}

//------------------------------------------------------------------------------

void FTLib::TransformNewOutput(std::shared_ptr<IComplexHermitianToReal> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData>& output, const FourierTransformAdditional* pAdditional)
{
	auto out = CBitmapData<CHeapAllocator>::Create(PixelType::GrayFloat32, input->GetWidth(), input->GetHeight());

	InternalTransform(transf, input, out, pAdditional);
	output = out;
}

/// <summary>	Fourier-transforms the real-valued source. The result is a newly allocated complex valued bitmap of
/// 			size (w/2+1) x h in "FFTW-packed" format.</summary>
///
/// <param name="transf">	The transf. </param>
/// <param name="input"> 	The input. </param>
/// <param name="output">	[in,out] The output. </param>
void FTLib::TransformNewOutput(std::shared_ptr<IRealToComplex_PackedFftwComplex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData>& output, const FourierTransformAdditional* pAdditional)
{
	uint32_t widthDest = input->GetWidth() / 2 + 1;

	auto out = CBitmapData<CHeapAllocator>::Create(PixelType::ComplexFloat32, widthDest, input->GetHeight());
	out->AddOrSetValueUint16(DATASTOREKEY_SEMANTICS, DS_SEMANTICS_FOURIERTRANSFORMED);
	out->AddOrSetValueUint8(DATASTOREKEY_FOURIERTRANSFORMED_LAYOUT, DS_FOURIERTRANSFORMED_LAYOUT_FULL_FFTWPACKED);

	InternalTransform(transf, input, out, pAdditional);
	output = out;
	/*
	auto srcBd = input->Lock();
	auto dstBd = output->Lock();

	FourierTransfRealInputData2d ftInp;
	ftInp.width = input->GetWidth();
	ftInp.height = input->GetHeight();
	ftInp.pitch = srcBd.pitch;
	ftInp.ptrData = (const float*)srcBd.ptrDataRoi;

	FourierTransfComplexOutput2d ftOutp;
	ftOutp.width = output->GetWidth();
	ftOutp.height = output->GetHeight();
	ftOutp.pitch = dstBd.pitch;
	ftOutp.ptrData = (float*)dstBd.ptrDataRoi;

	transf->RealToComplex_PackedFftwComplex(ftInp, ftOutp);

	input->Unlock();
	output->Unlock();*/
}

void FTLib::TransformNewOutput(std::shared_ptr<IRealToComplex_PackedIppComplex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData>& output, const FourierTransformAdditional* pAdditional)
{
	auto out = CBitmapData<CHeapAllocator>::Create(PixelType::GrayFloat32, input->GetWidth(), input->GetHeight());
	out->AddOrSetValueUint16(DATASTOREKEY_SEMANTICS, DS_SEMANTICS_FOURIERTRANSFORMED);
	out->AddOrSetValueUint8(DATASTOREKEY_FOURIERTRANSFORMED_LAYOUT, DS_FOURIERTRANSFORMED_LAYOUT_FULL_IPPPACKED);

	InternalTransform(transf, input, out, pAdditional);
	output = out;

	/*auto srcBd = input->Lock();
	auto dstBd = output->Lock();

	FourierTransfRealInputData2d ftInp;
	ftInp.width = input->GetWidth();
	ftInp.height = input->GetHeight();
	ftInp.pitch = srcBd.pitch;
	ftInp.ptrData = (const float*)srcBd.ptrDataRoi;

	FourierTransfComplexOutput2d ftOutp;
	ftOutp.width = output->GetWidth();
	ftOutp.height = output->GetHeight();
	ftOutp.pitch = dstBd.pitch;
	ftOutp.ptrData = (float*)dstBd.ptrDataRoi;

	transf->RealToComplex_PackedIppComplex(ftInp, ftOutp);

	input->Unlock();
	output->Unlock();*/
}

void FTLib::TransformNewOutput(std::shared_ptr<IRealToComplex_Complex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData>& output, const FourierTransformAdditional* pAdditional)
{
	auto out = CBitmapData<CHeapAllocator>::Create(PixelType::ComplexFloat32, input->GetWidth(), input->GetHeight());
	out->AddOrSetValueUint16(DATASTOREKEY_SEMANTICS, DS_SEMANTICS_FOURIERTRANSFORMED);
	out->AddOrSetValueUint8(DATASTOREKEY_FOURIERTRANSFORMED_LAYOUT, DS_FOURIERTRANSFORMED_LAYOUT_FULL_REALIMAGINTERLEAVED);

	InternalTransform(transf, input, out, pAdditional);
	output = out;

	//auto srcBd = input->Lock();
	//auto dstBd = output->Lock();

	//FourierTransfRealInputData2d ftInp;
	//ftInp.width = input->GetWidth();
	//ftInp.height = input->GetHeight();
	//ftInp.pitch = srcBd.pitch;
	//ftInp.ptrData = (const float*)srcBd.ptrDataRoi;

	//FourierTransfComplexOutput2d ftOutp;
	//ftOutp.width = output->GetWidth();
	//ftOutp.height = output->GetHeight();
	//ftOutp.pitch = dstBd.pitch;
	//ftOutp.ptrData = (float*)dstBd.ptrDataRoi;

	//transf->RealToComplex_Complex(ftInp, ftOutp);

	//input->Unlock();
	//output->Unlock();
}

void FTLib::TransformNewOutput(std::shared_ptr<IComplexToComplexFwd_Complex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData>& output, const FourierTransformAdditional* pAdditional)
{
	auto out = CBitmapData<CHeapAllocator>::Create(PixelType::ComplexFloat32, input->GetWidth(), input->GetHeight());
	out->AddOrSetValueUint16(DATASTOREKEY_SEMANTICS, DS_SEMANTICS_FOURIERTRANSFORMED);

	InternalTransform(transf, input, out, pAdditional);
	output = out;

	/*auto srcBd = input->Lock();
	auto dstBd = output->Lock();

	FourierTransfComplexInput2d ftInp;
	ftInp.width = input->GetWidth();
	ftInp.height = input->GetHeight();
	ftInp.pitch = srcBd.pitch;
	ftInp.ptrData = (const float*)srcBd.ptrDataRoi;

	FourierTransfComplexOutput2d ftOutp;
	ftOutp.width = output->GetWidth();
	ftOutp.height = output->GetHeight();
	ftOutp.pitch = dstBd.pitch;
	ftOutp.ptrData = (float*)dstBd.ptrDataRoi;

	transf->ComplexToComplex_Complex(ftInp, ftOutp);

	input->Unlock();
	output->Unlock();*/
}

void FTLib::TransformNewOutput(std::shared_ptr<IComplexToComplexInv_Complex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData>& output, const FourierTransformAdditional* pAdditional)
{
	auto out = CBitmapData<CHeapAllocator>::Create(PixelType::ComplexFloat32, input->GetWidth(), input->GetHeight());
	out->AddOrSetValueUint16(DATASTOREKEY_SEMANTICS, DS_SEMANTICS_FOURIERTRANSFORMED);

	InternalTransform(transf, input, out, pAdditional);
	output = out;

	//auto srcBd = input->Lock();
	//auto dstBd = output->Lock();

	//FourierTransfComplexInput2d ftInp;
	//ftInp.width = input->GetWidth();
	//ftInp.height = input->GetHeight();
	//ftInp.pitch = srcBd.pitch;
	//ftInp.ptrData = (const float*)srcBd.ptrDataRoi;

	//FourierTransfComplexOutput2d ftOutp;
	//ftOutp.width = output->GetWidth();
	//ftOutp.height = output->GetHeight();
	//ftOutp.pitch = dstBd.pitch;
	//ftOutp.ptrData = (float*)dstBd.ptrDataRoi;

	//transf->ComplexToComplex_Complex(ftInp, ftOutp);

	//input->Unlock();
	//output->Unlock();
}

//------------------------------------------------------------------------------
void FTLib::Transform(std::shared_ptr<IRealToComplex_PackedFftwComplex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	// TODO: check if input and output are suitable
	InternalTransform(transf, input, output, pAdditional);
}

void FTLib::Transform(std::shared_ptr<IRealToComplex_PackedIppComplex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	// TODO: check if input and output are suitable
	InternalTransform(transf, input, output, pAdditional);
}

void FTLib::Transform(std::shared_ptr<IRealToComplex_Complex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	// TODO: check if input and output are suitable
	InternalTransform(transf, input, output, pAdditional);
}

void FTLib::Transform(std::shared_ptr<IComplexToComplexFwd_Complex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	// TODO: check if input and output are suitable
	InternalTransform(transf, input, output, pAdditional);
}

void FTLib::Transform(std::shared_ptr<IComplexToComplexInv_Complex> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	// TODO: check if input and output are suitable
	InternalTransform(transf, input, output, pAdditional);
	// TODO: check if input and output are suitable
}

void FTLib::Transform(std::shared_ptr<IComplexHermitianToReal> transf, std::shared_ptr<IBitmapData> input, std::shared_ptr<IBitmapData> output, const FourierTransformAdditional* pAdditional)
{
	// TODO: check if input and output are suitable
	InternalTransform(transf, input, output, pAdditional);
}